package com.module.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.study.mybatisplus.base.BaseEntity;

public class GeneratorUtil {

	private static final Logger log = LoggerFactory
			.getLogger(GeneratorUtil.class);

	private static final String TIME_STR = "time";

	/**
	 * 项目路径
	 */
	private static final String PROJECT_DIR = System.getProperty("user.dir");

	/**
	 * 代码生成方法
	 *
	 * @param url
	 *            数据库连接url
	 * @param username
	 *            数据库用户名
	 * @param mapperPackagePath
	 *            mapper类的包路径，同时用于xml文件的namespace
	 * @param entityPackagePath
	 *            entity类的包路径，同时用于xml文件的resultMap的type
	 * @param password
	 *            数据库连接密码
	 * @param javaBeanOutputDir
	 *            java文件输出的根路径, 指定到项目的/src/main/java目录即可。 实体类和mapper类的具体路径即:
	 *            javaBeanOutputDir + entityPackagePath(mapperPackagePath)
	 * @param tables
	 *            需要生成的表 多张表用,隔开
	 */
	public static void generate(String url, String username, String password,
			String tables, String mapperPackagePath, String entityPackagePath,
			String javaBeanOutputDir, String superServiceClass,
			String superMapperClass, String superServiceImplClass) {
		// 代码生成器
		AutoGenerator autoGenerator = new AutoGenerator();
		// set freemarker engine
		autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());

		GlobalConfig globalConfig = buildGlobalConfig(javaBeanOutputDir);
		autoGenerator.setGlobalConfig(globalConfig);

		DataSourceConfig dataSourceConfig = buildDataSourceConfig(url, username,
				password, "com.mysql.cj.jdbc.Driver");
		autoGenerator.setDataSource(dataSourceConfig);

		InjectionConfig injectionConfig = buildInjectionConfig(PROJECT_DIR);
		autoGenerator.setCfg(injectionConfig);

		PackageConfig packageConfig = buildPackageConfig(mapperPackagePath,
				entityPackagePath);
		autoGenerator.setPackageInfo(packageConfig);

		TemplateConfig templateConfig = buildTemplateConfig(null,
				superMapperClass, superServiceClass, superServiceImplClass,
				null);
		autoGenerator.setTemplate(templateConfig);

		StrategyConfig strategyConfig = buildStrategyConfig(BaseEntity.class,
				superServiceClass, superMapperClass, superServiceImplClass,
				tables);
		autoGenerator.setStrategy(strategyConfig);

		autoGenerator.execute();
	}

	/**
	 * 构建全局配置
	 */
	public static GlobalConfig buildGlobalConfig(String javaBeanOutputDir) {
		GlobalConfig globalConfig = new GlobalConfig();
		// 全局配置
		globalConfig.setOutputDir(javaBeanOutputDir);
		// 设置 是否打开输出目录（false不会直接打开）
		globalConfig.setOpen(false);
		// 是否覆盖文件
		globalConfig.setFileOverride(true);
		// 设置注解的作者
		globalConfig.setAuthor("lian xin");
		// 设置注解的时间
		globalConfig.setDateType(DateType.ONLY_DATE);
		// 开启 BaseResultMap
		globalConfig.setBaseResultMap(true);
		// 开启 BaseColumnList
		globalConfig.setBaseColumnList(true);
		// 设置Service名字，%s会自定转化成对应实体名
		globalConfig.setServiceName("%sService");
		return globalConfig;
	}

	/**
	 * 数据源配置
	 *
	 * @param url
	 *            url
	 * @param username
	 *            用户名
	 * @param password
	 *            密码
	 * @param driverName
	 *            驱动
	 */
	public static DataSourceConfig buildDataSourceConfig(String url,
			String username, String password, String driverName) {
		// 数据源配置
		DataSourceConfig dataSourceConfig = new DataSourceConfig();
		dataSourceConfig.setUrl(url);
		dataSourceConfig.setDriverName(driverName);
		dataSourceConfig.setUsername(username);
		dataSourceConfig.setPassword(password);
		dataSourceConfig.setTypeConvert(new MySqlTypeConvert() {
			@Override
			public DbColumnType processTypeConvert(GlobalConfig globalConfig,
					String fieldType) {
				// time 转换成 LOCAL_DATE_TIME
				log.info("注册mybatis plus 生成转化器 数据库表字段类型 ：{}", fieldType);
				if (fieldType.toLowerCase().contains(TIME_STR)) {
					return DbColumnType.LOCAL_DATE_TIME;
				}
				return (DbColumnType) super.processTypeConvert(globalConfig,
						fieldType);
			}
		});
		return dataSourceConfig;
	}

	/**
	 * 包路径配置
	 *
	 * @param mapperPackagePath
	 *            mapper类的包路径，同时用于xml文件的namespace
	 * @param entityPackagePath
	 *            entity类的包路径，同时用于xml文件的resultMap的type
	 */
	public static PackageConfig buildPackageConfig(String mapperPackagePath,
			String entityPackagePath) {
		// java路径下的父包名配置
		PackageConfig packageConfig = new PackageConfig();
		packageConfig.setParent("");
		packageConfig.setMapper(mapperPackagePath);
		packageConfig.setEntity(entityPackagePath);
		return packageConfig;
	}

	/**
	 * 自定义配置
	 *
	 * @param projectPath
	 *            当前项目的路径
	 */
	public static InjectionConfig buildInjectionConfig(String projectPath) {
		// 自定义配置
		InjectionConfig injectionConfig = new InjectionConfig() {
			@Override
			public void initMap() {
				// to do nothing
			}
		};

		// 如果模板引擎是 freemarker
		String templatePath = "/templates/mapper.xml.ftl";

		// 自定义输出配置
		List<FileOutConfig> focList = new ArrayList<>();
		// 自定义配置会被优先输出
		focList.add(new FileOutConfig(templatePath) {
			@Override
			public String outputFile(TableInfo tableInfo) {
				// 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
				return projectPath + "/src/main/resources/mybatis/mapper/" + "/"
						+ tableInfo.getEntityName() + "Mapper"
						+ StringPool.DOT_XML;
			}
		});
		injectionConfig.setFileOutConfigList(focList);
		return injectionConfig;
	}

	/**
	 * 模板配置 是否需要 controller service 等 不需要传null
	 *
	 * @param controller
	 *            controller
	 * @param mapper
	 *            mapper
	 * @param service
	 *            service
	 * @param serviceImpl
	 *            serviceImpl
	 * @param xml
	 *            xml
	 */
	public static TemplateConfig buildTemplateConfig(String controller,
			String mapper, String service, String serviceImpl, String xml) {
		// 配置模板
		TemplateConfig templateConfig = new TemplateConfig();
		// 控制 不生成 controller,直接“”或者填null,其他的类似设置
		templateConfig.setController(controller);
		templateConfig.setMapper(mapper);
		templateConfig.setServiceImpl(service);
		templateConfig.setService(serviceImpl);
		// 设置为空才不会直接在持久层输出xml映射文件
		templateConfig.setXml(xml);
		return templateConfig;
	}

	/**
	 * 生成策略构建
	 *
	 * @param superClass
	 *            entity 父类
	 * @param superServiceClass
	 *            service 父类
	 * @param superMapperClass
	 *            mapper 父类
	 * @param superServiceImplClass
	 *            serviceImpl 父类
	 * @param tables
	 *            tables 表名
	 */
	public static StrategyConfig buildStrategyConfig(Class<?> superClass,
			String superServiceClass, String superMapperClass,
			String superServiceImplClass, String tables) {
		StrategyConfig strategy = new StrategyConfig();
		strategy.setNaming(NamingStrategy.underline_to_camel);
		strategy.setColumnNaming(NamingStrategy.underline_to_camel);
		// 是否开启lombok模式
		strategy.setEntityLombokModel(false);
		strategy.setRestControllerStyle(true);
		// 公共父类的路径
		if (Objects.nonNull(superClass)) {
			strategy.setSuperEntityClass(superClass);
		}
		strategy.setEntitySerialVersionUID(false);
		strategy.setSuperMapperClass(superMapperClass);
		strategy.setSuperServiceClass(superServiceClass);
		strategy.setSuperServiceImplClass(superServiceImplClass);

		// 写于父类中的公共字段
		strategy.setSuperEntityColumns("id", "updated_by", "updated_time",
				"created_by", "created_time", "state");

		strategy.setInclude(tables.split(","));
		strategy.setControllerMappingHyphenStyle(true);
		strategy.setTablePrefix("t_", "oauth_");
		return strategy;
	}
}
